[POWERPC] Enable SMP, smp_processor_id, for_each_cpu, nosmp, maxcpus=X
authorJimi Xenidis <jimix@watson.ibm.com>
Tue, 22 Aug 2006 13:53:34 +0000 (09:53 -0400)
committerJimi Xenidis <jimix@watson.ibm.com>
Tue, 22 Aug 2006 13:53:34 +0000 (09:53 -0400)
Add support for the "nosmp" and "maxcpus=X" command line options, and
address Hollis' concerns about comments, prototypes, and panic messages.

Handshake with secondary processors, move all of them out of their OF
spinloop even if there are more of them than NR_CPUS, enumerate them
according to a logical rather than firmware numerical space, give them a
stack, make them run C code to initialize their HIDs, initialize their
r13 registers to a per-processor pointer, enable smp_processor_id and
and properly set the maps of present and online CPUs.

Tested on 2-way JS20, 4-way JS21, and 16-way systemsim-gpul, with
NR_CPUS from 1 to 64, nosmp, and maxcpus from 1 to 3.

Signed-off-by: Amos Waterland <apw@us.ibm.com>
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
xen/arch/powerpc/boot_of.c
xen/arch/powerpc/powerpc64/exceptions.S
xen/arch/powerpc/powerpc64/ppc970.c
xen/arch/powerpc/setup.c
xen/include/asm-powerpc/config.h
xen/include/asm-powerpc/current.h
xen/include/asm-powerpc/powerpc64/procarea.h
xen/include/asm-powerpc/processor.h
xen/include/asm-powerpc/smp.h

index a75370421837fe1bfb3df62febef120878440135..855befa11e2ad5227b2c1e9c9ebcc59357b236c2 100644 (file)
@@ -32,6 +32,9 @@
 #include "exceptions.h"
 #include "of-devtree.h"
 
+/* Secondary processors use this for handshaking with main processor.  */
+volatile unsigned int __spin_ack;
+
 static ulong of_vec;
 static ulong of_msr;
 static int of_out;
@@ -955,7 +958,7 @@ static void boot_of_module(ulong r3, ulong r4, multiboot_info_t *mbi)
 static int __init boot_of_cpus(void)
 {
     int cpus;
-    int cpu;
+    int cpu, bootcpu, logical;
     int result;
     u32 cpu_clock[2];
 
@@ -980,10 +983,65 @@ static int __init boot_of_cpus(void)
     cpu_khz /= 1000;
     of_printf("OF: clock-frequency = %ld KHz\n", cpu_khz);
 
-    /* FIXME: should not depend on the boot CPU bring the first child */
+    /* Look up which CPU we are running on right now.  */
+    result = of_getprop(bof_chosen, "cpu", &bootcpu, sizeof (bootcpu));
+    if (result == OF_FAILURE)
+        of_panic("Failed to look up boot cpu\n");
+
     cpu = of_getpeer(cpu);
-    while (cpu > 0) {
-        of_start_cpu(cpu, (ulong)spin_start, 0);
+
+    /* We want a continuous logical cpu number space.  */
+    cpu_set(0, cpu_present_map);
+    cpu_set(0, cpu_online_map);
+
+    /* Spin up all CPUS, even if there are more than NR_CPUS, because
+     * Open Firmware has them spinning on cache lines which will
+     * eventually be scrubbed, which could lead to random CPU activation.
+     */
+    for (logical = 1; cpu > 0; logical++) {
+        unsigned int cpuid, ping, pong;
+        unsigned long now, then, timeout;
+
+        if (cpu == bootcpu) {
+            of_printf("skipping boot cpu!\n");
+            continue;
+        }
+
+        result = of_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
+        if (result == OF_FAILURE)
+            of_panic("cpuid lookup failed\n");
+
+        of_printf("spinning up secondary processor #%d: ", logical);
+
+        __spin_ack = ~0x0;
+        ping = __spin_ack;
+        pong = __spin_ack;
+        of_printf("ping = 0x%x: ", ping);
+
+        mb();
+        result = of_start_cpu(cpu, (ulong)spin_start, logical);
+        if (result == OF_FAILURE)
+            of_panic("start cpu failed\n");
+
+        /* We will give the secondary processor five seconds to reply.  */
+        then = mftb();
+        timeout = then + (5 * timebase_freq);
+
+        do {
+            now = mftb();
+            if (now >= timeout) {
+                of_printf("BROKEN: ");
+                break;
+            }
+
+            mb();
+            pong = __spin_ack;
+        } while (pong == ping);
+        of_printf("pong = 0x%x\n", pong);
+
+        if (pong != ping)
+            cpu_set(logical, cpu_present_map);
+
         cpu = of_getpeer(cpu);
     }
     return 1;
@@ -1031,6 +1089,7 @@ multiboot_info_t __init *boot_of_init(
     boot_of_rtas();
 
     /* end of OF */
+    of_printf("Quiescing Open Firmware ...\n");
     of_call("quiesce", 0, 0, NULL);
 
     return &mbi;
index 94181a1a242b047dc1ca2b71c747cac545a390ef..acda124b75ed6c9baf6593cb9c0a58b7f4a5f04f 100644 (file)
@@ -514,6 +514,43 @@ _GLOBAL(sleep)
     mtmsrd r3
     blr
 
+/* The primary processor issues a firmware call to spin us up at this
+ * address, passing our CPU number in r3.  We only need a function
+ * entry point instead of a descriptor since this is never called from
+ * C code.
+ */    
     .globl spin_start
 spin_start:
+    /* Write our processor number as an acknowledgment that we're alive.  */
+    LOADADDR(r14, __spin_ack)
+    stw r3, 0(r14)
+    sync
+    /* If NR_CPUS is too small, we should just spin forever.  */
+    LOADADDR(r15, NR_CPUS)
+    cmpd r3, r15
+    blt 2f     
+    b .
+    /* Find our index in the array of processor_area struct pointers.  */
+2:  LOADADDR(r14, global_cpu_table)
+    muli r15, r3, 8
+    add r14, r14, r15
+    /* Spin until the pointer for our processor goes valid.  */
+1:  ld r15, 0(r14)
+    cmpldi r15, 0
+    beq 1b
+    /* Dereference the pointer and load our stack pointer.  */
+    isync
+    ld r1, PAREA_stack(r15)
+    li r14, STACK_FRAME_OVERHEAD
+    sub r1, r1, r14
+    /* Load up the TOC and entry point for the C function to be called.  */
+    LOADADDR(r14, secondary_cpu_init)
+    ld r2, 8(r14)
+    ld r11, 0(r14)
+    mtctr r11
+    /* Warning: why do we need this synchronizing instruction on 970FX?  */
+    isync
+    /* Jump into C code now.  */
+    bctrl
+    nop
     b .
index f5d15512b5e3e5a0858f6c9e79101ba30972e565..da1b65426aa09bac318e9296ccff588707be656a 100644 (file)
@@ -31,6 +31,8 @@
 
 #undef SERIALIZE
 
+extern volatile struct processor_area * volatile global_cpu_table[];
+
 unsigned int cpu_rma_order(void)
 {
     /* XXX what about non-HV mode? */
@@ -38,19 +40,16 @@ unsigned int cpu_rma_order(void)
     return rma_log_size - PAGE_SHIFT;
 }
 
-void cpu_initialize(void)
+void cpu_initialize(int cpuid)
 {
-    ulong stack;
+    ulong r1, r2;
+    __asm__ __volatile__ ("mr %0, 1" : "=r" (r1));
+    __asm__ __volatile__ ("mr %0, 2" : "=r" (r2));
 
-    parea = xmalloc(struct processor_area);
+    /* This is SMP safe because the compiler must use r13 for it.  */
+    parea = global_cpu_table[cpuid];
     ASSERT(parea != NULL);
 
-    stack = (ulong)alloc_xenheap_pages(STACK_ORDER);
-
-    ASSERT(stack != 0);
-    parea->hyp_stack_base = (void *)(stack + STACK_SIZE);
-    printk("stack is here: %p\n", parea->hyp_stack_base);
-
     mthsprg0((ulong)parea); /* now ready for exceptions */
 
     /* Set decrementers for 1 second to keep them out of the way during
@@ -79,7 +78,10 @@ void cpu_initialize(void)
     s |= 1UL << (63-3);     /* ser-gp */
     hid0.word |= s;
 #endif
-    printk("hid0: 0x%016lx\n", hid0.word);
+
+    printk("CPU #%d: Hello World! SP = %lx TOC = %lx HID0 = %lx\n", 
+           smp_processor_id(), r1, r2, hid0.word);
+
     mthid0(hid0.word);
 
     union hid1 hid1;
index 21782178dbadfa994e7a88c40da5cb71043d4563..cdc2173229ef127a7559e2e7b173ee2bf0f6ef5a 100644 (file)
@@ -54,6 +54,14 @@ boolean_param("noht", opt_noht);
 int opt_earlygdb = 0;
 boolean_param("earlygdb", opt_earlygdb);
 
+/* opt_nosmp: If true, secondary processors are ignored. */
+static int opt_nosmp = 0;
+boolean_param("nosmp", opt_nosmp);
+
+/* maxcpus: maximum number of CPUs to activate. */
+static unsigned int max_cpus = NR_CPUS;
+integer_param("maxcpus", max_cpus);
+
 u32 tlbflush_clock = 1U;
 DEFINE_PER_CPU(u32, tlbflush_time);
 
@@ -65,6 +73,7 @@ ulong oftree_end;
 
 cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly;
 cpumask_t cpu_online_map; /* missing ifdef in schedule.c */
+cpumask_t cpu_present_map;
 
 /* XXX get this from ISA node in device tree */
 ulong isa_io_base;
@@ -76,6 +85,8 @@ extern void idle_loop(void);
 /* move us to a header file */
 extern void initialize_keytable(void);
 
+volatile struct processor_area * volatile global_cpu_table[NR_CPUS];
+
 int is_kernel_text(unsigned long addr)
 {
     if (addr >= (unsigned long) &_start &&
@@ -212,6 +223,53 @@ static ulong free_xenheap(ulong start, ulong end)
     return ALIGN_UP(end, PAGE_SIZE);
 }
 
+static void init_parea(int cpuid)
+{
+    /* Be careful not to shadow the global variable.  */
+    volatile struct processor_area *pa;
+    void *stack;
+
+    pa = xmalloc(struct processor_area);
+    if (pa == NULL)
+        panic("%s: failed to allocate parea for cpu #%d\n", __func__, cpuid);
+
+    stack = alloc_xenheap_pages(STACK_ORDER);
+    if (stack == NULL)
+        panic("%s: failed to allocate stack (order %d) for cpu #%d\n", 
+              __func__, STACK_ORDER, cpuid);
+
+    pa->whoami = cpuid;
+    pa->hyp_stack_base = (void *)((ulong)stack + STACK_SIZE);
+
+    /* This store has the effect of invoking secondary_cpu_init.  */
+    global_cpu_table[cpuid] = pa;
+    mb();
+}
+
+static int kick_secondary_cpus(int maxcpus)
+{
+    int cpuid;
+
+    for_each_present_cpu(cpuid) {
+        if (cpuid == 0)
+            continue;
+        if (cpuid >= maxcpus)
+            break;
+        init_parea(cpuid);
+        cpu_set(cpuid, cpu_online_map);
+    }
+
+    return 0;
+}
+
+/* This is the first C code that secondary processors invoke.  */
+int secondary_cpu_init(int cpuid, unsigned long r4);
+int secondary_cpu_init(int cpuid, unsigned long r4)
+{
+    cpu_initialize(cpuid);
+    while(1);
+}
+
 static void __init __start_xen(multiboot_info_t *mbi)
 {
     char *cmdline;
@@ -361,7 +419,8 @@ static void __init __start_xen(multiboot_info_t *mbi)
 
     percpu_init_areas();
 
-    cpu_initialize();
+    init_parea(0);
+    cpu_initialize(0);
 
 #ifdef CONFIG_GDB
     initialise_gdb();
@@ -369,6 +428,14 @@ static void __init __start_xen(multiboot_info_t *mbi)
         debugger_trap_immediate();
 #endif
 
+    /* Deal with secondary processors.  */
+    if (opt_nosmp) {
+        printk("nosmp: leaving secondary processors spinning forever\n");
+    } else {
+        printk("spinning up at most %d total processors ...\n", max_cpus);
+        kick_secondary_cpus(max_cpus);
+    }
+
     start_of_day();
 
     /* Create initial domain 0. */
@@ -446,6 +513,8 @@ void arch_get_xen_caps(xen_capabilities_info_t info)
 {
 }
 
+
+
 /*
  * Local variables:
  * mode: C
index ff28fc7de5130632391e2f5631fc080b296e94e2..1c081a90ff0911791531288a6197c04a9aa42be8 100644 (file)
@@ -51,7 +51,7 @@ extern char __bss_start[];
 #define CONFIG_GDB 1
 #define CONFIG_SMP 1
 #define CONFIG_PCI 1
-#define NR_CPUS 1
+#define NR_CPUS 16
 
 #ifndef ELFSIZE
 #define ELFSIZE 64
index 639dfe9ec5e430ee64bc340e307001063f0c6c8b..f30aad20e2cf4688a623c272daf50ce15dfb2615 100644 (file)
@@ -27,7 +27,7 @@
 
 struct vcpu;
 
-register struct processor_area *parea asm("r13");
+register volatile struct processor_area *parea asm("r13");
 
 static inline struct vcpu *get_current(void)
 {
index 9715df3b352288c218ccdc904d7685f4ab6f3065..11f91b67e6edee3ef48fe25187f4f7e780fddfef 100644 (file)
@@ -28,6 +28,7 @@ struct gdb_state;
 
 struct processor_area
 {
+    unsigned int whoami;
     struct vcpu *cur_vcpu;
     void *hyp_stack_base;
     ulong saved_regs[2];
index 863cb194f2cd68835b1425a8c2bbd56993f39a86..db9bd9bca39e3f58b9b87f6c8359c7d13b9e7066 100644 (file)
@@ -40,7 +40,7 @@ struct cpu_user_regs;
 extern void show_registers(struct cpu_user_regs *);
 extern void show_execution_state(struct cpu_user_regs *);
 extern unsigned int cpu_rma_order(void);
-extern void cpu_initialize(void);
+extern void cpu_initialize(int cpuid);
 extern void cpu_init_vcpu(struct vcpu *);
 extern void save_cpu_sprs(struct vcpu *);
 extern void load_cpu_sprs(struct vcpu *);
index 6010aac99ae8a9e24652353805bb5e2aa2f34e7d..7c65789eacc9669cdae150e4382c62fd83fdd38c 100644 (file)
@@ -28,8 +28,8 @@ extern int smp_num_siblings;
 
 /* revisit when we support SMP */
 #define get_hard_smp_processor_id(i) i
-#define hard_smp_processor_id() 0
-#define raw_smp_processor_id() 0
+#define raw_smp_processor_id() (parea->whoami)
+#define hard_smp_processor_id() raw_smp_processor_id()
 extern cpumask_t cpu_sibling_map[];
 extern cpumask_t cpu_core_map[];